package com.wenbing.miio.core;

import com.wenbing.miio.core.exception.NetworkLayerException;
import com.wenbing.miio.util.BytesUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 *
 * @author wen bing
 * @projectName: miio
 * @package: com.wenbing.miio.core
 * @className: AbstractNetworkStackFrame
 * @date 2024/09/19 14:19
 *
 *
 * 抽象网络层，定义处理流程
 * 入栈
 *      生成本层Context -> 复制上下文必要参数 -> 解码 -> 交付下层处理
 * 出栈
 *      生成/获取本层Context -> 编码 -> 复制上下文必要参数 -> 交付上层处理
 **/
public abstract class AbstractNetworkLayer implements NetworkLayer {

    protected final Logger LOGGER = LoggerFactory.getLogger(getClass());

    /**
     * 所属节点
     */
    private NetworkStack.NetworkLayerNode networkLayerNode;

    protected AbstractNetworkLayer() {
        this.networkLayerNode = new NetworkStack.NetworkLayerNode();
        this.networkLayerNode.setLayer(this);
    }

    /**
     * 解包数据
     * @param context
     * @param packingData
     * @return
     * @throws IOException
     * @throws NetworkLayerException
     */
    protected abstract ByteBuffer unpacking(NetworkLayerContext context, ByteBuffer packingData) throws IOException, NetworkLayerException;

    /**
     * 交付给下层程序进行处理
     * @param context
     * @param packingData
     * @return
     */
    protected void deliverLowerLayer(NetworkLayerContext context, ByteBuffer packingData) throws NetworkLayerException, IOException {
        // 交由下层协议处理
        NetworkLayer lowerLayer = networkLayerNode.getLowerLayer();
        if (NetworkLayer.valid(lowerLayer)) {
            LOGGER.info("deliver to the next layer");
            lowerLayer.inbound(context, packingData);
        }
    }

    @Override
    public void inbound(NetworkLayerContext parentContext, ByteBuffer packingData) throws IOException, NetworkLayerException {
        NetworkLayerContext context = new MapNetworkLayerContext(parentContext, parentContext.getShareContext());
        inboundContextCopy(parentContext, context);
        packingData = unpacking(context, packingData);
        log("inbound --> independent", context, packingData);
        // 交由下层协议处理
        deliverLowerLayer(context, packingData);
    }

    /**
     * 封装数据包
     * @param context
     * @param packingData
     * @return
     * @throws IOException
     * @throws NetworkLayerException
     */
    protected abstract ByteBuffer packing(NetworkLayerContext context, ByteBuffer packingData) throws IOException, NetworkLayerException;

    /**
     * 交付给上层程序进行处理
     * @param context
     * @param packingData
     * @return
     */
    protected void deliverUpperLayer(NetworkLayerContext context, ByteBuffer packingData) throws NetworkLayerException, IOException {
        // 交由上层协议处理
        NetworkLayer upperLayer = networkLayerNode.getUpperLayer();
        if (NetworkLayer.valid(upperLayer)) {
            LOGGER.info("deliver to the previous layer");
            upperLayer.outbound(context, packingData);
        }
    }

    @Override
    public void outbound(NetworkLayerContext context, ByteBuffer unpackingData) throws IOException, NetworkLayerException {
        assert context != null;
        unpackingData = packing(context, unpackingData);
        log("outbound --> independent", context, unpackingData);
        NetworkLayerContext parent = context.getParentContext();
        if (parent == null) {
            parent = new MapNetworkLayerContext(null, context.getShareContext());
        }
        outboundContextCopy(context, parent);
        // 交由下层协议处理
        deliverUpperLayer(parent, unpackingData);
    }

    /**
     * 入栈方向的上下文 数据复制
     * @param from
     * @param to
     */
    protected void inboundContextCopy(NetworkLayerContext from, NetworkLayerContext to) {

    }

    /**
     * 出栈方向的上下文 数据复制
     * @param from
     * @param to
     */
    protected void outboundContextCopy(NetworkLayerContext from, NetworkLayerContext to) {

    }

    /**
     * 日志记录
     * @param target
     * @param context
     * @param unpackingData
     */
    protected void log(String target, NetworkLayerContext context, ByteBuffer unpackingData) {
        LOGGER.debug("{} context, data length is {} bytes",target, unpackingData.limit());
        if (LOGGER.isTraceEnabled()) {
            String hexData = BytesUtils.bytes2Hex(unpackingData);
            LOGGER.trace("data is {}", hexData.isEmpty() ? null : hexData);
            LOGGER.trace("context is {}", context);
            LOGGER.trace("share context is {}", context.getShareContext());
        }
    }
    @Override
    public NetworkStack.NetworkLayerNode getNetworkLayerNode() {
        return this.networkLayerNode;
    }
}
